Background
Rob Baker et al fit growth parameters to B. rapa growth data and then used those to do function-value-trait QTL mapping.
I used those same parameters and queried a mutual rank (MR) gene expression network to find genes that were in a networks associated with these growth paramters. This was done separately for the CR and UN environments. I looked for overlap between those MR-associated genes and growth parameters.
The goal now is to find the trans eQTL for each of the MR-growth associated genes and ask if the trans eQTL overlap with the growth/function-value QTL. I think I am only going to take the top eQTL for each.
Focused on UN genes only.
This is for the CIM eQTL results
Methodology
For each gene in a MR network with a growth parameter query the eQTL database to find its trans eQTL regions. Then compare overlaps between those and the growth QTL.
Load the libraries and data
Libraries
library(qtl)
library(GenomicRanges)
library(tidyverse)
library(magrittr)
Growth QTL
filepath <- "../input/All2012HeightQTL2.xlsx"
filebase <- filepath %>% basename() %>% str_replace("\\..*$","")
QTLgenes <- readxl::read_excel(filepath)[,-1]
New names:
* `` -> ...1
QTLgenes <- QTLgenes %>% dplyr::rename(.id=QTL, FVTtrait=FVT) # change names to match previous file
QTLgenes <- QTLgenes %>% filter(str_detect(FVTtrait,"^UN"))
QTLgenes
MR genes from UN
MR_UN_genes <- read_csv("../output/MR_UN_graphs_node_annotation_2012.csv") %>%
filter(MR_Cutoff <= 50, !duplicated(name)) %>%
mutate(pos=floor((start+end)/2)) %>%
select(MR_Cutoff,name, transcript_chrom=chrom, transcript_pos=pos)
Parsed with column specification:
cols(
.default = col_double(),
.id = [31mcol_character()[39m,
trt = [31mcol_character()[39m,
name = [31mcol_character()[39m,
chrom = [31mcol_character()[39m,
subject = [31mcol_character()[39m,
AGI = [31mcol_character()[39m,
At_symbol = [31mcol_character()[39m,
At_description = [31mcol_character()[39m
)
See spec(...) for full column specifications.
MR_UN_genes
eQTL
load("../output/scanone-MRgene-qtl_2012.RData")
scanone_CIM_UN <- scanone_MR_cim
scanone_CIM_UN <- scanone_CIM_UN[!str_detect(rownames(scanone_CIM_UN),"loc"),] #downstream code cannot deal with interpolated markers.
Find eQTL intervals
UN data
Get the scanone data in a nice format for summarizing and plotting
scanone_UN_gather <- scanone_CIM_UN %>%
gather(key = gene, value = LOD, -chr, -pos) %>%
right_join(MR_UN_genes,by=c("gene"="name")) # only keep genes in MR networks
Alternate thredhold.
previously I calcualted the 0.05 threshold of each gene separately and then took the average. What if I calculate the experimenwise threshold?
dim(permtest.cim)
[1] 1000 56
This represents the 1000 permutations for each gene. So to get an experiment wise threshold I could take the max for each row and then to the 95% of that.
newthreshold <- permtest.cim %>% apply(1,max) %>% quantile(0.95)
newthreshold
95%
6.949558
For MR30, this should only include the MR30 genes
MR30genes <- MR_UN_genes %>% filter(MR_Cutoff <=30) %>% pull(name)
newthresholdMR30 <- permtest.cim[, MR30genes] %>%
apply(1,max) %>%
quantile(0.95)
newthresholdMR30
95%
6.564742
plot eQTL peaks…
pl.UN <- scanone_UN_gather %>%
ggplot(aes(x=pos,y=LOD,color=gene)) +
geom_line() +
geom_hline(aes(yintercept=newthreshold),lty=2,lwd=.5,alpha=.5) +
facet_grid( ~ chr, scales="free") +
theme(strip.text.y = element_text(angle=0), axis.text.x = element_text(angle=90)) +
ggtitle("MR gene eQTL")
pl.UN

ggsave(str_c("../output/MR50 gene eQTL UN CIM", Sys.Date(), ".pdf"),width=12,height=8)
ggsave(str_c("../output/MR50 gene eQTL UN CIM", Sys.Date(), ".png"),width=10,height=5)
pl.UN + coord_cartesian(ylim=c(0,10))

plot eQTL peaks for MR30
pl.UN <- scanone_UN_gather %>%
filter(MR_Cutoff <=30) %>%
ggplot(aes(x=pos,y=LOD,color=gene)) +
geom_line() +
geom_hline(aes(yintercept=newthresholdMR30),lty=2,lwd=.5,alpha=.5) +
facet_grid( ~ chr, scales="free") +
theme(strip.text.y = element_text(angle=0), axis.text.x = element_text(angle=90)) +
ggtitle("MR gene eQTL")
pl.UN

ggsave(str_c("../output/MR30 gene eQTL UN CIM", Sys.Date(), ".pdf"),width=12,height=8)
ggsave(str_c("../output/MR30 gene eQTL UN CIM", Sys.Date(), ".png"),width=10,height=5)
cis and trans plot
scanone_UN_gather %>%
arrange(transcript_chrom,transcript_pos,pos) %>%
filter(LOD>newthreshold) %>%
group_by(gene,chr) %>%
filter(LOD==max(LOD)) %>%
ungroup() %>%
mutate(transcript_index=row_number(),cis_trans=ifelse(chr==transcript_chrom,"cis","trans")) %>%
ggplot(aes(x=pos,y=transcript_index,shape=cis_trans,color=LOD)) +
scale_color_gradient(low="magenta1",high="magenta4") +
geom_point() +
facet_wrap(~chr,nrow=1) +
theme_bw() +
xlab("QTL position")
ggsave(str_c("../output/MR_gene_eQTL_cistrans_UN_CIM_", Sys.Date(), ".png"),width=10,height = 5)

sig_chromosomes_UN <- scanone_UN_gather %>%
group_by(gene,chr) %>%
summarize(pos=pos[which.max(LOD)],LOD=max(LOD)) %>%
filter(LOD > newthreshold)
sig_chromosomes_UN
now for each significant chromosome/trait combo run bayesint
bayesint_list_UN <- apply(sig_chromosomes_UN,1,function(hit) {
result <- bayesint(scanone_CIM_UN[c("chr","pos",hit["gene"])],
chr=hit["chr"],
lodcolumn = 1,
prob=0.99,
expandtomarkers = TRUE
)
colnames(result)[3] <- "LOD"
result
})
names(bayesint_list_UN) <- sig_chromosomes_UN$gene
bayesint_list_UN <- lapply(bayesint_list_UN,function(x) x %>%
as.data.frame(stringsAsFactors=FALSE) %>%
rownames_to_column(var="markername") %>%
mutate(chr=as.character(chr))
)
bayesint_result_UN <- as.tibble(bind_rows(bayesint_list_UN,.id="gene")) %>%
select(gene,chr,pos,markername,LOD) %>%
separate(markername,into=c("chr1","Mbp"),sep="x", convert=TRUE) %>%
group_by(gene,chr) %>%
summarize(eQTL_start_bp=min(Mbp), eQTL_end_bp=max(Mbp), eQTL_start_cM=min(pos), eQTL_end_cM=max(pos), min_eQTL_LOD=min(LOD), max_eQTL_LOD=max(LOD)) %>%
#for the high QTL peaks the interval width is 0. That is overly precise and need to widen those.
mutate(eQTL_start_bp=ifelse(eQTL_start_bp==eQTL_end_bp ,max(0, eQTL_start_bp - 20000), eQTL_start_bp), eQTL_end_bp=ifelse(eQTL_start_bp==eQTL_end_bp, eQTL_end_bp + 20000, eQTL_end_bp))
`as.tibble()` is deprecated, use `as_tibble()` (but mind the new semantics).
[90mThis warning is displayed once per session.[39m
bayesint_result_UN
annotate UN eQTL
Load annotation
BrapaAnnotation <- read_csv("../input/Brapa_V1.5_annotated.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
X1 = [32mcol_double()[39m,
name = [31mcol_character()[39m,
chrom = [31mcol_character()[39m,
start = [32mcol_double()[39m,
end = [32mcol_double()[39m,
subject = [31mcol_character()[39m,
AGI = [31mcol_character()[39m,
At_symbol = [31mcol_character()[39m,
At_description = [31mcol_character()[39m,
perc_ID = [32mcol_double()[39m,
aln_length = [32mcol_double()[39m,
mismatch = [32mcol_double()[39m,
gap_open = [32mcol_double()[39m,
qstart = [32mcol_double()[39m,
qend = [32mcol_double()[39m,
sstart = [32mcol_double()[39m,
send = [32mcol_double()[39m,
eval = [32mcol_double()[39m,
score = [32mcol_double()[39m
)
BrapaAnnotation
UN_annotated <- lapply(1:nrow(bayesint_result_UN),function(row) {
qtl <- bayesint_result_UN[row,]
subset(BrapaAnnotation, chrom==qtl$chr &
start >= qtl$eQTL_start_bp &
end <= qtl$eQTL_end_bp)
}
)
names(UN_annotated) <- bayesint_result_UN$gene
UN_annotated <- bind_rows(UN_annotated,.id="MR_gene") %>%
left_join(bayesint_result_UN,by=c("MR_gene"="gene","chrom"="chr")) %>% #get eQTL LOD
left_join(MR_UN_genes,by=c("MR_gene"="name")) %>% # get cutoff
dplyr::rename(eQTL_candidate=name)
UN_annotated_small <- UN_annotated %>% select(MR_gene,MR_Cutoff,MR_chrom=transcript_chrom,MR_pos=transcript_pos,eQTL_chrom=chrom, eQTL_start_bp, eQTL_end_bp, eQTL_start_cM, eQTL_end_cM, eQTL_candidate,ends_with("LOD"))
UN_annotated_small
write_csv(UN_annotated_small, path=str_c("../output/",
filebase,
"_MR_eQTL_UN_ALL_CIM_",
Sys.Date(), ".csv"))
cis eQTL? Because the BayesInt intervals may be too small, define cis as anything on the same chromosome
UN_annotated_small %>%
filter(MR_chrom==eQTL_chrom) %>%
filter(!duplicated(MR_gene))
total # of MR genes with cis eQTL
#total MR genes with an eQTL at all:
cat("total MR genes with an eQTL:", {
scanone_UN_gather %>%
filter(LOD > newthreshold) %>%
filter(!duplicated(gene)) %>%
nrow()})
total MR genes with an eQTL: 40
#total MR genes with a cis eQTL
cat("\ntotal MR genes with a cis eQTL:", {
UN_annotated_small %>%
filter(MR_chrom==eQTL_chrom) %>%
filter(!duplicated(MR_gene)) %>%
nrow()
})
total MR genes with a cis eQTL: 29
number of genes with a trans eQTL:
##FILL THIS IN
given bayesint results, find overlaps with UN growth QTL
UN_MReQTL_QTL_combined <- inner_join(QTLgenes,UN_annotated_small,by=c("name"="eQTL_candidate")) %>%
select(.id, MR_gene, MR_Cutoff, eQTL_start_bp, eQTL_end_bp, ends_with("LOD"), everything()) %>%
arrange(.id,desc(max_eQTL_LOD)) %>%
dplyr::rename(eQTL_candidate=name)
UN_MReQTL_QTL_combined
UN_MReQTL_QTL_combined_small <- UN_MReQTL_QTL_combined %>% filter(!duplicated(eQTL_candidate)) %>%
select(-MR_gene,-MR_Cutoff)
UN_MReQTL_QTL_combined_small
number of cis eQTL in overlapping regions
UN_MReQTL_QTL_combined %>%
filter(MR_gene==eQTL_candidate) %>%
filter(!duplicated(MR_gene)) %>%
nrow()
[1] 6
Total number of MR genes with an eQTL that overlaps with an FVT
UN_MReQTL_QTL_combined %>% select(MR_gene) %>% unique() %>% nrow()
[1] 37
how many QTL have at least some overlap?
length(unique(QTLgenes$.id))
[1] 16
length(unique(UN_MReQTL_QTL_combined$.id))
[1] 8
8 of 16
rr write_csv(UN_MReQTL_QTL_combined, path=str_c(../output/, filebase, _MR_eQTL_UN_overlap_CIM_, Sys.Date(), .csv))
How to assess if overlap is significant?
I think pull regions of same size as eQTL and ask how often they overlap with growth QTL.
For each eQTL, randomly select a chromosome, then a position, and widen based on interval. Then check overlap. Repeat.
Make table of chromosome info
chr.info <- scanone_CIM_UN %>%
as.data.frame() %>%
rownames_to_column("marker") %>%
select(marker) %>%
separate(marker,into=c("chr","bp"),sep="x",convert=TRUE) %>%
group_by(chr) %>%
summarize(start=min(bp),end=max(bp))
Make a table of QTL info
qtl.info <- QTLgenes %>%
group_by(.id) %>%
summarize(chrom=unique(chrom),start=min(start),end=max(end))
qtl.info
qtl.ranges <- GRanges(seqnames = qtl.info$chrom,ranges=IRanges(start=qtl.info$start,end=qtl.info$end))
qtl.ranges <- GenomicRanges::reduce(qtl.ranges)
The eQTL are in Bayesint_results
sim.results
[1] 0 0 1 3 1 1 2 4 1 0 2 1 2 2 0 1 3 1 4 1 4 2 2 0 0 0 1 1 1 2 2 0 2 0 3 0 0 2 2 1 3 2 1 0 2
[46] 2 1 2 2 1 0 3 2 2 0 2 0 2 0 0 1 0 2 3 1 0 2 2 3 0 0 3 3 1 0 2 2 2 1 2 0 2 2 0 3 1 1 5 2 0
[91] 2 1 2 3 3 1 0 1 1 4 2 0 2 1 1 2 2 3 0 1 1 0 1 3 1 0 0 4 1 2 3 0 2 1 1 2 2 1 0 2 0 3 2 0 3
[136] 1 1 0 0 1 0 2 1 1 0 2 1 3 0 1 1 1 0 2 1 1 3 1 0 1 1 2 1 2 1 0 2 2 1 0 2 3 1 1 1 1 1 1 4 1
[181] 2 2 0 0 1 0 0 0 0 1 1 0 3 1 2 0 2 2 2 0 3 1 2 1 1 2 0 2 1 2 2 3 1 3 1 1 1 2 1 2 0 1 2 0 0
[226] 3 0 1 1 4 2 2 2 0 1 3 1 1 2 3 3 3 1 0 0 2 1 3 0 2 2 1 2 3 1 3 1 0 0 2 0 0 1 3 0 0 3 2 1 0
[271] 1 2 2 0 2 0 1 1 3 2 1 1 1 0 0 4 1 0 3 0 1 1 1 2 2 0 1 1 1 2 0 1 1 1 2 0 0 3 3 1 3 0 1 1 1
[316] 1 1 2 0 1 2 1 1 3 0 3 1 0 2 0 2 1 2 1 3 0 0 1 2 0 0 3 0 2 3 3 3 1 3 2 2 3 0 0 1 1 1 1 1 2
[361] 1 3 1 2 3 2 1 1 2 1 0 4 2 3 2 1 1 0 0 2 2 1 4 1 2 2 2 1 3 1 1 0 1 2 2 1 4 2 3 2 2 2 1 2 2
[406] 0 1 2 0 3 1 1 2 2 1 0 2 1 2 1 4 2 1 2 2 2 2 1 0 2 1 2 2 2 1 1 2 1 0 0 1 2 1 1 3 2 2 2 2 2
[451] 1 1 3 3 0 0 1 0 2 2 2 2 1 1 1 2 0 0 3 1 1 1 4 2 0 2 2 0 1 0 1 1 1 2 2 1 1 1 0 2 2 4 1 4 2
[496] 1 3 1 0 3 2 2 1 0 4 2 2 0 0 2 2 1 3 1 1 1 3 4 0 1 1 0 2 1 1 2 1 1 0 1 1 3 0 1 2 2 1 1 3 4
[541] 2 2 2 0 2 1 1 2 0 2 0 0 1 3 0 1 0 1 1 1 2 0 3 3 0 1 0 2 1 1 1 2 1 1 1 2 1 3 0 2 0 1 0 2 1
[586] 0 0 3 2 1 3 0 2 1 3 2 1 2 3 1 3 1 1 0 0 2 4 2 0 1 2 2 2 2 1 0 2 0 4 2 1 0 1 1 0 2 2 1 3 2
[631] 3 2 2 0 2 1 1 0 1 2 3 1 0 1 1 2 2 3 1 0 2 1 1 1 1 2 1 1 3 1 1 1 0 2 1 3 1 1 0 1 2 1 1 0 2
[676] 2 4 2 0 2 2 1 0 1 1 1 0 0 2 2 0 1 3 2 1 1 3 2 1 0 2 0 2 0 0 0 1 1 1 2 0 3 1 1 0 2 0 3 2 2
[721] 3 3 0 4 3 1 0 1 1 3 3 1 0 2 0 0 1 0 1 2 2 1 1 1 2 1 1 2 2 3 1 1 0 1 0 1 1 1 1 0 1 0 0 4 0
[766] 0 1 2 2 3 0 1 1 1 1 3 2 2 3 0 0 1 1 1 1 1 4 1 0 0 1 0 0 2 3 3 1 3 2 1 2 3 2 1 0 1 0 2 2 0
[811] 3 1 1 0 0 0 1 1 1 2 1 2 1 3 0 2 0 0 1 4 3 1 1 1 1 3 1 1 2 2 3 1 1 2 1 0 1 2 1 1 0 1 2 1 2
[856] 0 1 1 1 3 1 1 0 0 0 0 2 3 3 3 1 1 2 0 1 1 2 1 3 1 1 0 0 0 1 1 0 1 2 2 2 1 1 1 2 1 3 0 1 1
[901] 2 1 3 1 1 0 0 4 2 3 1 3 2 3 1 4 1 1 2 1 1 3 2 1 2 0 0 3 0 3 0 0 1 1 3 1 1 1 3 0 0 2 2 1 3
[946] 1 0 2 2 2 3 3 1 1 1 2 0 0 2 1 1 0 1 2 3 5 2 1 0 0 2 1 1 2 2 1 0 0 0 1 1 0 2 3 2 0 0 1 1 0
[991] 2 3 1 2 3 1 0 1 2 2
mean(sim.results >= true.overlap)
[1] 0
significant; 0 of the simulations had as many overlaps as in the true data set.
LS0tCnRpdGxlOiAiTXV0dWFsIFJhbmsgZVFUTCBvdmVybGFwIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyBCYWNrZ3JvdW5kCgpSb2IgQmFrZXIgZXQgYWwgZml0IGdyb3d0aCBwYXJhbWV0ZXJzIHRvIEIuIHJhcGEgZ3Jvd3RoIGRhdGEgYW5kIHRoZW4gdXNlZCB0aG9zZSB0byBkbyBmdW5jdGlvbi12YWx1ZS10cmFpdCBRVEwgbWFwcGluZy4KCkkgdXNlZCB0aG9zZSBzYW1lIHBhcmFtZXRlcnMgYW5kIHF1ZXJpZWQgYSBtdXR1YWwgcmFuayAoTVIpIGdlbmUgZXhwcmVzc2lvbiBuZXR3b3JrIHRvIGZpbmQgZ2VuZXMgdGhhdCB3ZXJlIGluIGEgbmV0d29ya3MgYXNzb2NpYXRlZCB3aXRoIHRoZXNlIGdyb3d0aCBwYXJhbXRlcnMuICBUaGlzIHdhcyBkb25lIHNlcGFyYXRlbHkgZm9yIHRoZSBDUiBhbmQgVU4gZW52aXJvbm1lbnRzLiAgSSBsb29rZWQgZm9yIG92ZXJsYXAgYmV0d2VlbiB0aG9zZSBNUi1hc3NvY2lhdGVkIGdlbmVzIGFuZCBncm93dGggcGFyYW1ldGVycy4KClRoZSBnb2FsIG5vdyBpcyB0byBmaW5kIHRoZSB0cmFucyBlUVRMIGZvciBlYWNoIG9mIHRoZSBNUi1ncm93dGggYXNzb2NpYXRlZCBnZW5lcyBhbmQgYXNrIGlmIHRoZSB0cmFucyBlUVRMIG92ZXJsYXAgd2l0aCB0aGUgZ3Jvd3RoL2Z1bmN0aW9uLXZhbHVlIFFUTC4gIEkgdGhpbmsgSSBhbSBvbmx5IGdvaW5nIHRvIHRha2UgdGhlIHRvcCBlUVRMIGZvciBlYWNoLgoKRm9jdXNlZCBvbiBVTiBnZW5lcyBvbmx5LgoKX19UaGlzIGlzIGZvciB0aGUgQ0lNIGVRVEwgcmVzdWx0c19fCgojIyBNZXRob2RvbG9neQoKRm9yIGVhY2ggZ2VuZSBpbiBhIE1SIG5ldHdvcmsgd2l0aCBhIGdyb3d0aCBwYXJhbWV0ZXIgcXVlcnkgdGhlIGVRVEwgZGF0YWJhc2UgdG8gZmluZCBpdHMgdHJhbnMgZVFUTCByZWdpb25zLiAgVGhlbiBjb21wYXJlIG92ZXJsYXBzIGJldHdlZW4gdGhvc2UgYW5kIHRoZSBncm93dGggUVRMLgoKIyMgTG9hZCB0aGUgbGlicmFyaWVzIGFuZCBkYXRhCgpMaWJyYXJpZXMKYGBge3J9CmxpYnJhcnkocXRsKQpsaWJyYXJ5KEdlbm9taWNSYW5nZXMpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KG1hZ3JpdHRyKQpgYGAKCkdyb3d0aCBRVEwKCmBgYHtyfQpmaWxlcGF0aCA8LSAiLi4vaW5wdXQvQWxsMjAxMkhlaWdodFFUTDIueGxzeCIKZmlsZWJhc2UgPC0gZmlsZXBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIHN0cl9yZXBsYWNlKCJcXC4uKiQiLCIiKQoKUVRMZ2VuZXMgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKGZpbGVwYXRoKVssLTFdClFUTGdlbmVzIDwtIFFUTGdlbmVzICU+JSBkcGx5cjo6cmVuYW1lKC5pZD1RVEwsIEZWVHRyYWl0PUZWVCkgIyBjaGFuZ2UgbmFtZXMgdG8gbWF0Y2ggcHJldmlvdXMgZmlsZQpRVExnZW5lcyA8LSBRVExnZW5lcyAlPiUgZmlsdGVyKHN0cl9kZXRlY3QoRlZUdHJhaXQsIl5VTiIpKQpRVExnZW5lcwpgYGAKCk1SIGdlbmVzIGZyb20gVU4KYGBge3J9Ck1SX1VOX2dlbmVzIDwtIHJlYWRfY3N2KCIuLi9vdXRwdXQvTVJfVU5fZ3JhcGhzX25vZGVfYW5ub3RhdGlvbl8yMDEyLmNzdiIpICU+JQogIGZpbHRlcihNUl9DdXRvZmYgPD0gNTAsICFkdXBsaWNhdGVkKG5hbWUpKSAlPiUKICBtdXRhdGUocG9zPWZsb29yKChzdGFydCtlbmQpLzIpKSAlPiUKICBzZWxlY3QoTVJfQ3V0b2ZmLG5hbWUsIHRyYW5zY3JpcHRfY2hyb209Y2hyb20sIHRyYW5zY3JpcHRfcG9zPXBvcykKTVJfVU5fZ2VuZXMKYGBgCgplUVRMCmBgYHtyfQpsb2FkKCIuLi9vdXRwdXQvc2Nhbm9uZS1NUmdlbmUtcXRsXzIwMTIuUkRhdGEiKQpzY2Fub25lX0NJTV9VTiA8LSBzY2Fub25lX01SX2NpbQpzY2Fub25lX0NJTV9VTiA8LSBzY2Fub25lX0NJTV9VTlshc3RyX2RldGVjdChyb3duYW1lcyhzY2Fub25lX0NJTV9VTiksImxvYyIpLF0gI2Rvd25zdHJlYW0gY29kZSBjYW5ub3QgZGVhbCB3aXRoIGludGVycG9sYXRlZCBtYXJrZXJzLgpgYGAKCiMjIEZpbmQgZVFUTCBpbnRlcnZhbHMKCgojIyMgVU4gZGF0YQoKR2V0IHRoZSBzY2Fub25lIGRhdGEgaW4gYSBuaWNlIGZvcm1hdCBmb3Igc3VtbWFyaXppbmcgYW5kIHBsb3R0aW5nCmBgYHtyfQpzY2Fub25lX1VOX2dhdGhlciA8LSBzY2Fub25lX0NJTV9VTiAlPiUKICBnYXRoZXIoa2V5ID0gZ2VuZSwgdmFsdWUgPSBMT0QsIC1jaHIsIC1wb3MpICU+JQogIHJpZ2h0X2pvaW4oTVJfVU5fZ2VuZXMsYnk9YygiZ2VuZSI9Im5hbWUiKSkgIyBvbmx5IGtlZXAgZ2VuZXMgaW4gTVIgbmV0d29ya3MKYGBgCgojIyMjIEFsdGVybmF0ZSB0aHJlZGhvbGQuCgpwcmV2aW91c2x5IEkgY2FsY3VhbHRlZCB0aGUgMC4wNSB0aHJlc2hvbGQgb2YgZWFjaCBnZW5lIHNlcGFyYXRlbHkgYW5kIHRoZW4gdG9vayB0aGUgYXZlcmFnZS4gIFdoYXQgaWYgSSBjYWxjdWxhdGUgdGhlIGV4cGVyaW1lbndpc2UgdGhyZXNob2xkPwoKYGBge3J9CmRpbShwZXJtdGVzdC5jaW0pIApgYGAKVGhpcyByZXByZXNlbnRzIHRoZSAxMDAwIHBlcm11dGF0aW9ucyBmb3IgZWFjaCBnZW5lLiAgU28gdG8gZ2V0IGFuIGV4cGVyaW1lbnQgd2lzZSB0aHJlc2hvbGQgSSBjb3VsZCB0YWtlIHRoZSBtYXggZm9yIGVhY2ggcm93IGFuZCB0aGVuIHRvIHRoZSA5NSUgb2YgdGhhdC4KCmBgYHtyfQpuZXd0aHJlc2hvbGQgPC0gcGVybXRlc3QuY2ltICU+JSBhcHBseSgxLG1heCkgJT4lIHF1YW50aWxlKDAuOTUpCm5ld3RocmVzaG9sZApgYGAKCkZvciBNUjMwLCB0aGlzIHNob3VsZCBvbmx5IGluY2x1ZGUgdGhlIE1SMzAgZ2VuZXMKCmBgYHtyfQpNUjMwZ2VuZXMgPC0gTVJfVU5fZ2VuZXMgJT4lIGZpbHRlcihNUl9DdXRvZmYgPD0zMCkgJT4lIHB1bGwobmFtZSkKCm5ld3RocmVzaG9sZE1SMzAgPC0gcGVybXRlc3QuY2ltWywgTVIzMGdlbmVzXSAlPiUKICBhcHBseSgxLG1heCkgJT4lCiAgcXVhbnRpbGUoMC45NSkKCm5ld3RocmVzaG9sZE1SMzAKYGBgCgoKCiMjIyMgcGxvdCBlUVRMIHBlYWtzLi4uCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpwbC5VTiA8LSBzY2Fub25lX1VOX2dhdGhlciAlPiUKICBnZ3Bsb3QoYWVzKHg9cG9zLHk9TE9ELGNvbG9yPWdlbmUpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9bmV3dGhyZXNob2xkKSxsdHk9Mixsd2Q9LjUsYWxwaGE9LjUpICsKICBmYWNldF9ncmlkKCB+IGNociwgc2NhbGVzPSJmcmVlIikgKwogIHRoZW1lKHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZT0wKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTApKSArCiAgZ2d0aXRsZSgiTVIgZ2VuZSBlUVRMIikKcGwuVU4KZ2dzYXZlKHN0cl9jKCIuLi9vdXRwdXQvTVI1MCBnZW5lIGVRVEwgVU4gQ0lNIiwgU3lzLkRhdGUoKSwgIi5wZGYiKSx3aWR0aD0xMixoZWlnaHQ9OCkKZ2dzYXZlKHN0cl9jKCIuLi9vdXRwdXQvTVI1MCBnZW5lIGVRVEwgVU4gQ0lNIiwgU3lzLkRhdGUoKSwgIi5wbmciKSx3aWR0aD0xMCxoZWlnaHQ9NSkKcGwuVU4gKyBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsMTApKQpgYGAKCnBsb3QgZVFUTCBwZWFrcyBmb3IgTVIzMApgYGB7ciwgZmlnLmhlaWdodD0xMH0KcGwuVU4gPC0gc2Nhbm9uZV9VTl9nYXRoZXIgJT4lCiAgZmlsdGVyKE1SX0N1dG9mZiA8PTMwKSAlPiUKICBnZ3Bsb3QoYWVzKHg9cG9zLHk9TE9ELGNvbG9yPWdlbmUpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQ9bmV3dGhyZXNob2xkTVIzMCksbHR5PTIsbHdkPS41LGFscGhhPS41KSArCiAgZmFjZXRfZ3JpZCggfiBjaHIsIHNjYWxlcz0iZnJlZSIpICsKICB0aGVtZShzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGU9MCksIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwKSkgKwogIGdndGl0bGUoIk1SIGdlbmUgZVFUTCIpCnBsLlVOCmdnc2F2ZShzdHJfYygiLi4vb3V0cHV0L01SMzAgZ2VuZSBlUVRMIFVOIENJTSIsIFN5cy5EYXRlKCksICIucGRmIiksd2lkdGg9MTIsaGVpZ2h0PTgpCmdnc2F2ZShzdHJfYygiLi4vb3V0cHV0L01SMzAgZ2VuZSBlUVRMIFVOIENJTSIsIFN5cy5EYXRlKCksICIucG5nIiksd2lkdGg9MTAsaGVpZ2h0PTUpCgpgYGAKCiMjIGNpcyBhbmQgdHJhbnMgcGxvdAoKYGBge3J9CnNjYW5vbmVfVU5fZ2F0aGVyICU+JQogIGFycmFuZ2UodHJhbnNjcmlwdF9jaHJvbSx0cmFuc2NyaXB0X3Bvcyxwb3MpICU+JQogIGZpbHRlcihMT0Q+bmV3dGhyZXNob2xkKSAlPiUKICBncm91cF9ieShnZW5lLGNocikgJT4lCiAgZmlsdGVyKExPRD09bWF4KExPRCkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUodHJhbnNjcmlwdF9pbmRleD1yb3dfbnVtYmVyKCksY2lzX3RyYW5zPWlmZWxzZShjaHI9PXRyYW5zY3JpcHRfY2hyb20sImNpcyIsInRyYW5zIikpICU+JQogIGdncGxvdChhZXMoeD1wb3MseT10cmFuc2NyaXB0X2luZGV4LHNoYXBlPWNpc190cmFucyxjb2xvcj1MT0QpKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93PSJtYWdlbnRhMSIsaGlnaD0ibWFnZW50YTQiKSArCiAgZ2VvbV9wb2ludCgpICsKICBmYWNldF93cmFwKH5jaHIsbnJvdz0xKSArCiAgdGhlbWVfYncoKSArIAogIHhsYWIoIlFUTCBwb3NpdGlvbiIpCmdnc2F2ZShzdHJfYygiLi4vb3V0cHV0L01SX2dlbmVfZVFUTF9jaXN0cmFuc19VTl9DSU1fIiwgU3lzLkRhdGUoKSwgIi5wbmciKSx3aWR0aD0xMCxoZWlnaHQgPSA1KQpgYGAKCgoKCgpgYGB7cn0Kc2lnX2Nocm9tb3NvbWVzX1VOIDwtIHNjYW5vbmVfVU5fZ2F0aGVyICU+JQogIAogIGdyb3VwX2J5KGdlbmUsY2hyKSAlPiUKICBzdW1tYXJpemUocG9zPXBvc1t3aGljaC5tYXgoTE9EKV0sTE9EPW1heChMT0QpKSAlPiUKICBmaWx0ZXIoTE9EID4gbmV3dGhyZXNob2xkKQoKc2lnX2Nocm9tb3NvbWVzX1VOCmBgYAoKbm93IGZvciBlYWNoIHNpZ25pZmljYW50IGNocm9tb3NvbWUvdHJhaXQgY29tYm8gcnVuIGJheWVzaW50CgpgYGB7cn0KYmF5ZXNpbnRfbGlzdF9VTiA8LSBhcHBseShzaWdfY2hyb21vc29tZXNfVU4sMSxmdW5jdGlvbihoaXQpIHsKICByZXN1bHQgPC0gYmF5ZXNpbnQoc2Nhbm9uZV9DSU1fVU5bYygiY2hyIiwicG9zIixoaXRbImdlbmUiXSldLCAKICAgICAgICAgICAgICAgICAgICAgY2hyPWhpdFsiY2hyIl0sIAogICAgICAgICAgICAgICAgICAgICBsb2Rjb2x1bW4gPSAxLAogICAgICAgICAgICAgICAgICAgICBwcm9iPTAuOTksCiAgICAgICAgICAgICAgICAgICAgIGV4cGFuZHRvbWFya2VycyA9IFRSVUUKICApCiAgY29sbmFtZXMocmVzdWx0KVszXSA8LSAiTE9EIgogIHJlc3VsdAp9KQpuYW1lcyhiYXllc2ludF9saXN0X1VOKSA8LSBzaWdfY2hyb21vc29tZXNfVU4kZ2VuZQoKYmF5ZXNpbnRfbGlzdF9VTiA8LSBsYXBwbHkoYmF5ZXNpbnRfbGlzdF9VTixmdW5jdGlvbih4KSB4ICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzX3RvX2NvbHVtbih2YXI9Im1hcmtlcm5hbWUiKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoY2hyPWFzLmNoYXJhY3RlcihjaHIpKQopCmBgYAoKYGBge3J9CmJheWVzaW50X3Jlc3VsdF9VTiA8LSBhcy50aWJibGUoYmluZF9yb3dzKGJheWVzaW50X2xpc3RfVU4sLmlkPSJnZW5lIikpICU+JSAKICBzZWxlY3QoZ2VuZSxjaHIscG9zLG1hcmtlcm5hbWUsTE9EKSAlPiUKICBzZXBhcmF0ZShtYXJrZXJuYW1lLGludG89YygiY2hyMSIsIk1icCIpLHNlcD0ieCIsIGNvbnZlcnQ9VFJVRSkgJT4lCiAgZ3JvdXBfYnkoZ2VuZSxjaHIpICU+JQogIAogIAogIHN1bW1hcml6ZShlUVRMX3N0YXJ0X2JwPW1pbihNYnApLCBlUVRMX2VuZF9icD1tYXgoTWJwKSwgZVFUTF9zdGFydF9jTT1taW4ocG9zKSwgZVFUTF9lbmRfY009bWF4KHBvcyksIG1pbl9lUVRMX0xPRD1taW4oTE9EKSwgbWF4X2VRVExfTE9EPW1heChMT0QpKSAlPiUgCiAgICAgIAogICNmb3IgdGhlIGhpZ2ggUVRMIHBlYWtzIHRoZSBpbnRlcnZhbCB3aWR0aCBpcyAwLiAgVGhhdCBpcyBvdmVybHkgcHJlY2lzZSBhbmQgbmVlZCB0byB3aWRlbiB0aG9zZS4KICBtdXRhdGUoZVFUTF9zdGFydF9icD1pZmVsc2UoZVFUTF9zdGFydF9icD09ZVFUTF9lbmRfYnAgLG1heCgwLCBlUVRMX3N0YXJ0X2JwIC0gMjAwMDApLCBlUVRMX3N0YXJ0X2JwKSwgZVFUTF9lbmRfYnA9aWZlbHNlKGVRVExfc3RhcnRfYnA9PWVRVExfZW5kX2JwLCBlUVRMX2VuZF9icCArIDIwMDAwLCBlUVRMX2VuZF9icCkpCiAgCgpiYXllc2ludF9yZXN1bHRfVU4KYGBgCgojIyBhbm5vdGF0ZSBVTiBlUVRMCgpMb2FkIGFubm90YXRpb24KYGBge3J9CkJyYXBhQW5ub3RhdGlvbiA8LSByZWFkX2NzdigiLi4vaW5wdXQvQnJhcGFfVjEuNV9hbm5vdGF0ZWQuY3N2IikKQnJhcGFBbm5vdGF0aW9uCmBgYAoKCmBgYHtyfQpVTl9hbm5vdGF0ZWQgPC0gbGFwcGx5KDE6bnJvdyhiYXllc2ludF9yZXN1bHRfVU4pLGZ1bmN0aW9uKHJvdykgewogIHF0bCA8LSBiYXllc2ludF9yZXN1bHRfVU5bcm93LF0KICBzdWJzZXQoQnJhcGFBbm5vdGF0aW9uLCBjaHJvbT09cXRsJGNociAmCiAgICAgICAgICAgc3RhcnQgPj0gcXRsJGVRVExfc3RhcnRfYnAgJgogICAgICAgICAgIGVuZCA8PSBxdGwkZVFUTF9lbmRfYnApIAp9CikKbmFtZXMoVU5fYW5ub3RhdGVkKSA8LSBiYXllc2ludF9yZXN1bHRfVU4kZ2VuZQpVTl9hbm5vdGF0ZWQgPC0gYmluZF9yb3dzKFVOX2Fubm90YXRlZCwuaWQ9Ik1SX2dlbmUiKSAlPiUKICBsZWZ0X2pvaW4oYmF5ZXNpbnRfcmVzdWx0X1VOLGJ5PWMoIk1SX2dlbmUiPSJnZW5lIiwiY2hyb20iPSJjaHIiKSkgJT4lICNnZXQgZVFUTCBMT0QKICBsZWZ0X2pvaW4oTVJfVU5fZ2VuZXMsYnk9YygiTVJfZ2VuZSI9Im5hbWUiKSkgJT4lICMgZ2V0IGN1dG9mZgogIGRwbHlyOjpyZW5hbWUoZVFUTF9jYW5kaWRhdGU9bmFtZSkKCgpVTl9hbm5vdGF0ZWRfc21hbGwgPC0gVU5fYW5ub3RhdGVkICU+JSBzZWxlY3QoTVJfZ2VuZSxNUl9DdXRvZmYsTVJfY2hyb209dHJhbnNjcmlwdF9jaHJvbSxNUl9wb3M9dHJhbnNjcmlwdF9wb3MsZVFUTF9jaHJvbT1jaHJvbSwgZVFUTF9zdGFydF9icCwgZVFUTF9lbmRfYnAsIGVRVExfc3RhcnRfY00sIGVRVExfZW5kX2NNLCBlUVRMX2NhbmRpZGF0ZSxlbmRzX3dpdGgoIkxPRCIpKQoKVU5fYW5ub3RhdGVkX3NtYWxsCgp3cml0ZV9jc3YoVU5fYW5ub3RhdGVkX3NtYWxsLCBwYXRoPXN0cl9jKCIuLi9vdXRwdXQvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsZWJhc2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJfTVJfZVFUTF9VTl9BTExfQ0lNXyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN5cy5EYXRlKCksICIuY3N2IikpCmBgYAoKY2lzIGVRVEw/IEJlY2F1c2UgdGhlIEJheWVzSW50IGludGVydmFscyBtYXkgYmUgdG9vIHNtYWxsLCBkZWZpbmUgY2lzIGFzIGFueXRoaW5nIG9uIHRoZSBzYW1lIGNocm9tb3NvbWUKYGBge3J9ClVOX2Fubm90YXRlZF9zbWFsbCAlPiUgCiAgZmlsdGVyKE1SX2Nocm9tPT1lUVRMX2Nocm9tKSAlPiUKICBmaWx0ZXIoIWR1cGxpY2F0ZWQoTVJfZ2VuZSkpCmBgYAoKdG90YWwgIyBvZiBNUiBnZW5lcyB3aXRoIGNpcyBlUVRMCmBgYHtyfQojdG90YWwgTVIgZ2VuZXMgd2l0aCBhbiBlUVRMIGF0IGFsbDoKY2F0KCJ0b3RhbCBNUiBnZW5lcyB3aXRoIGFuIGVRVEw6IiwgewogIHNjYW5vbmVfVU5fZ2F0aGVyICU+JQogIGZpbHRlcihMT0QgPiBuZXd0aHJlc2hvbGQpICU+JQogIGZpbHRlcighZHVwbGljYXRlZChnZW5lKSkgJT4lCiAgbnJvdygpfSkKCiN0b3RhbCBNUiBnZW5lcyB3aXRoIGEgY2lzIGVRVEwKY2F0KCJcbnRvdGFsIE1SIGdlbmVzIHdpdGggYSBjaXMgZVFUTDoiLCB7CiAgVU5fYW5ub3RhdGVkX3NtYWxsICU+JSAKICBmaWx0ZXIoTVJfY2hyb209PWVRVExfY2hyb20pICU+JQogIGZpbHRlcighZHVwbGljYXRlZChNUl9nZW5lKSkgJT4lCiAgbnJvdygpCn0pCgoKYGBgCgpudW1iZXIgb2YgZ2VuZXMgd2l0aCBhIHRyYW5zIGVRVEw6CgpgYGB7cn0KIyNGSUxMIFRISVMgSU4KYGBgCgoKZ2l2ZW4gYmF5ZXNpbnQgcmVzdWx0cywgZmluZCBvdmVybGFwcyB3aXRoIFVOIGdyb3d0aCBRVEwKCmBgYHtyfQpVTl9NUmVRVExfUVRMX2NvbWJpbmVkIDwtIGlubmVyX2pvaW4oUVRMZ2VuZXMsVU5fYW5ub3RhdGVkX3NtYWxsLGJ5PWMoIm5hbWUiPSJlUVRMX2NhbmRpZGF0ZSIpKSAlPiUKICBzZWxlY3QoLmlkLCBNUl9nZW5lLCBNUl9DdXRvZmYsIGVRVExfc3RhcnRfYnAsIGVRVExfZW5kX2JwLCBlbmRzX3dpdGgoIkxPRCIpLCBldmVyeXRoaW5nKCkpICU+JQogIGFycmFuZ2UoLmlkLGRlc2MobWF4X2VRVExfTE9EKSkgJT4lCiAgZHBseXI6OnJlbmFtZShlUVRMX2NhbmRpZGF0ZT1uYW1lKQpVTl9NUmVRVExfUVRMX2NvbWJpbmVkCgpVTl9NUmVRVExfUVRMX2NvbWJpbmVkX3NtYWxsIDwtIFVOX01SZVFUTF9RVExfY29tYmluZWQgJT4lIGZpbHRlcighZHVwbGljYXRlZChlUVRMX2NhbmRpZGF0ZSkpICU+JQogIHNlbGVjdCgtTVJfZ2VuZSwtTVJfQ3V0b2ZmKSAKVU5fTVJlUVRMX1FUTF9jb21iaW5lZF9zbWFsbApgYGAKCm51bWJlciBvZiBjaXMgZVFUTCBpbiBvdmVybGFwcGluZyByZWdpb25zCmBgYHtyfQpVTl9NUmVRVExfUVRMX2NvbWJpbmVkICU+JSAKICBmaWx0ZXIoTVJfZ2VuZT09ZVFUTF9jYW5kaWRhdGUpICU+JSAKICBmaWx0ZXIoIWR1cGxpY2F0ZWQoTVJfZ2VuZSkpICU+JQogIG5yb3coKQpgYGAKClRvdGFsIG51bWJlciBvZiBNUiBnZW5lcyB3aXRoIGFuIGVRVEwgdGhhdCBvdmVybGFwcyB3aXRoIGFuIEZWVApgYGB7cn0KVU5fTVJlUVRMX1FUTF9jb21iaW5lZCAlPiUgc2VsZWN0KE1SX2dlbmUpICU+JSB1bmlxdWUoKSAlPiUgbnJvdygpCmBgYAoKCmhvdyBtYW55IFFUTCBoYXZlIGF0IGxlYXN0IHNvbWUgb3ZlcmxhcD8KYGBge3J9Cmxlbmd0aCh1bmlxdWUoUVRMZ2VuZXMkLmlkKSkKbGVuZ3RoKHVuaXF1ZShVTl9NUmVRVExfUVRMX2NvbWJpbmVkJC5pZCkpCmBgYAoKOCBvZiAxNgoKYGBge3J9CndyaXRlX2NzdihVTl9NUmVRVExfUVRMX2NvbWJpbmVkLAogICAgICAgICAgcGF0aD1zdHJfYygiLi4vb3V0cHV0LyIsIGZpbGViYXNlLCAiX01SX2VRVExfVU5fb3ZlcmxhcF9DSU1fIiwgU3lzLkRhdGUoKSwgIi5jc3YiKSkKYGBgCgpIb3cgdG8gYXNzZXNzIGlmIG92ZXJsYXAgaXMgc2lnbmlmaWNhbnQ/CgpJIHRoaW5rIHB1bGwgcmVnaW9ucyBvZiBzYW1lIHNpemUgYXMgZVFUTCBhbmQgYXNrIGhvdyBvZnRlbiB0aGV5IG92ZXJsYXAgd2l0aCBncm93dGggUVRMLgoKRm9yIGVhY2ggZVFUTCwgcmFuZG9tbHkgc2VsZWN0IGEgY2hyb21vc29tZSwgdGhlbiBhIHBvc2l0aW9uLCBhbmQgd2lkZW4gYmFzZWQgb24gaW50ZXJ2YWwuICBUaGVuIGNoZWNrIG92ZXJsYXAuICBSZXBlYXQuCgpNYWtlIHRhYmxlIG9mIGNocm9tb3NvbWUgaW5mbwpgYGB7cn0KY2hyLmluZm8gPC0gc2Nhbm9uZV9DSU1fVU4gJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oIm1hcmtlciIpICU+JQogIHNlbGVjdChtYXJrZXIpICU+JQogIHNlcGFyYXRlKG1hcmtlcixpbnRvPWMoImNociIsImJwIiksc2VwPSJ4Iixjb252ZXJ0PVRSVUUpICU+JQogIGdyb3VwX2J5KGNocikgJT4lCiAgc3VtbWFyaXplKHN0YXJ0PW1pbihicCksZW5kPW1heChicCkpCmBgYAoKTWFrZSBhIHRhYmxlIG9mIFFUTCBpbmZvCgpgYGB7cn0KcXRsLmluZm8gPC0gUVRMZ2VuZXMgJT4lCiAgZ3JvdXBfYnkoLmlkKSAlPiUKICBzdW1tYXJpemUoY2hyb209dW5pcXVlKGNocm9tKSxzdGFydD1taW4oc3RhcnQpLGVuZD1tYXgoZW5kKSkKcXRsLmluZm8KcXRsLnJhbmdlcyA8LSBHUmFuZ2VzKHNlcW5hbWVzID0gcXRsLmluZm8kY2hyb20scmFuZ2VzPUlSYW5nZXMoc3RhcnQ9cXRsLmluZm8kc3RhcnQsZW5kPXF0bC5pbmZvJGVuZCkpCnF0bC5yYW5nZXMgPC0gR2Vub21pY1Jhbmdlczo6cmVkdWNlKHF0bC5yYW5nZXMpCmBgYAoKClRoZSBlUVRMIGFyZSBpbiBCYXllc2ludF9yZXN1bHRzCgoKYGBge3J9CnNpbXMgPC0gMTAwMAplUVRMLnJhbmdlcyA8LSBHUmFuZ2VzKGJheWVzaW50X3Jlc3VsdF9VTiRjaHIsCiAgICAgICAgICAgICAgICAgICAgICAgcmFuZ2VzID0gSVJhbmdlcyhzdGFydD1iYXllc2ludF9yZXN1bHRfVU4kZVFUTF9zdGFydF9icCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZD1iYXllc2ludF9yZXN1bHRfVU4kZVFUTF9lbmRfYnApKQplUVRMLnJhbmdlcyA8LSBHZW5vbWljUmFuZ2VzOjpyZWR1Y2UoZVFUTC5yYW5nZXMpCgpzZXQuc2VlZCg1NDMyMSkKc2ltLnJlc3VsdHMgPC0gc2FwcGx5KDE6c2ltcywgZnVuY3Rpb24ocykgewogIHNpbS5lUVRMIDwtIHRpYmJsZSgKICAgIGNocj1zYW1wbGUoY2hyLmluZm8kY2hyLAogICAgICAgICAgICAgICBzaXplID0gbGVuZ3RoKGVRVEwucmFuZ2VzKSwKICAgICAgICAgICAgICAgcmVwbGFjZSA9IFRSVUUsCiAgICAgICAgICAgICAgIHByb2I9Y2hyLmluZm8kZW5kL3N1bShjaHIuaW5mbyRlbmQpKSwKICAgIHdpZHRoPXdpZHRoKGVRVEwucmFuZ2VzKSAjIHdpZHRoIG9mIHRoZSBRVEwgdG8gc2ltdWxhdGUKICApCiAgc2ltLmVRVEwgPC0gY2hyLmluZm8gJT4lIAogICAgc2VsZWN0KGNocixjaHIuc3RhcnQ9c3RhcnQsY2hyLmVuZD1lbmQpICU+JSByaWdodF9qb2luKHNpbS5lUVRMLGJ5PSJjaHIiKSAjbmVlZCB0byBnZXQgdGhlIGNocm9tIGVuZCBzbyB3ZSBjYW4gc2FtcGxlIGNvcnJlY3RseQogIHNpbS5lUVRMIDwtIHNpbS5lUVRMICU+JSBtdXRhdGUocXRsLnN0YXJ0ID0gcnVuaWYobj1uKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4gPSBjaHIuc3RhcnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXg9IG1heChjaHIuc3RhcnQsY2hyLmVuZC13aWR0aCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXRsLmVuZD1xdGwuc3RhcnQrd2lkdGgpCiAgc2ltLmVRVEwucmFuZ2VzIDwtIEdSYW5nZXMoc2VxbmFtZXMgPSBzaW0uZVFUTCRjaHIscmFuZ2VzID0gSVJhbmdlcyhzdGFydD1zaW0uZVFUTCRxdGwuc3RhcnQsZW5kPXNpbS5lUVRMJHF0bC5lbmQpKQogIHN1cHByZXNzV2FybmluZ3MocmVzdWx0IDwtIHN1bShjb3VudE92ZXJsYXBzKHF0bC5yYW5nZXMsc2ltLmVRVEwucmFuZ2VzKT4wKSkKICByZXN1bHQKfSkKCmBgYAoKCmBgYHtyfQp0cnVlLm92ZXJsYXAgPC0gc3VtKGNvdW50T3ZlcmxhcHMocXRsLnJhbmdlcyxlUVRMLnJhbmdlcykpICNPSyB0byBpZ25vcmUgd2FybmluZ3MKCnRydWUub3ZlcmxhcAoKbWVhbihzaW0ucmVzdWx0cyA+PSB0cnVlLm92ZXJsYXApCgp0aWJibGUoRlZUUVRMX3ZzX01SZVFUTF9UcnVlX092ZXJsYXBzPXRydWUub3ZlcmxhcCwKICAgICAgIE5fU2ltdWxhdGlvbnNfZmV3ZXJfb3ZlcmxhcHM9c3VtKHNpbS5yZXN1bHRzIDwgdHJ1ZS5vdmVybGFwKSwKICAgICAgIE5fU2ltdWxhdGlvbnNfZ3JlYXRlcl9lcXVhbF9vdmVybGFwcz1zdW0oc2ltLnJlc3VsdHMgPj0gdHJ1ZS5vdmVybGFwKSwKICAgICAgIFBfdmFsdWU9bWVhbihzaW0ucmVzdWx0cyA+PSB0cnVlLm92ZXJsYXApCikgJT4lCiAgd3JpdGVfY3N2KHN0cl9jKCIuLi9vdXRwdXQvIiwgZmlsZWJhc2UsICJfTVJlUVRMX292ZXJsYXBfcHZhbF9DSU0iLCBTeXMuRGF0ZSgpLCAiLmNzdiIpKQpgYGAKCnNpZ25pZmljYW50OyAwIG9mIHRoZSBzaW11bGF0aW9ucyBoYWQgYXMgbWFueSBvdmVybGFwcyBhcyBpbiB0aGUgdHJ1ZSBkYXRhIHNldC4K